package com.stox.client.ui.javafx.chartingy.model.axis;

import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.control.Label;
import javafx.scene.layout.Region;
import javafx.scene.text.Text;

import com.stox.client.ui.javafx.chartingy.model.chart.Chart;
import com.stox.client.ui.javafx.core.util.CSS;
import com.stox.client.ui.javafx.core.util.UI;
import com.stox.shared.util.Constant;

public class ValueAxis extends Region {
	private static double textHeight = new Text("1").getLayoutBounds().getHeight();
	private static final double padding = 5;
	private static final double tickLength = 6;
	public static final double width = 60;
	private static final double[] TICK_UNIT_DEFAULTS = {1.0E-10d, 2.5E-10d, 5.0E-10d, 1.0E-9d, 2.5E-9d, 5.0E-9d, 1.0E-8d, 2.5E-8d, 5.0E-8d, 1.0E-7d, 2.5E-7d, 5.0E-7d, 1.0E-6d, 2.5E-6d, 5.0E-6d, 1.0E-5d, 2.5E-5d, 5.0E-5d, 1.0E-4d, 2.5E-4d, 5.0E-4d, 0.0010d, 0.0025d, 0.0050d, 0.01d, 0.025d, 0.05d, 0.1d, 0.25d, 0.5d, 1.0d, 2.5d, 5.0d, 10.0d, 25.0d, 50.0d, 100.0d, 250.0d, 500.0d, 1000.0d, 2500.0d, 5000.0d, 10000.0d, 25000.0d, 50000.0d, 100000.0d, 250000.0d, 500000.0d, 1000000.0d, 2500000.0d, 5000000.0d, 1.0E7d, 2.5E7d, 5.0E7d, 1.0E8d, 2.5E8d, 5.0E8d, 1.0E9d, 2.5E9d, 5.0E9d, 1.0E10d, 2.5E10d, 5.0E10d, 1.0E11d, 2.5E11d, 5.0E11d, 1.0E12d, 2.5E12d, 5.0E12d};

	private boolean zoomable = false;
	private final Chart<?, ?> chart;
	private double upperBound = 0;
	private double lowerBound = 0;
	private double scale = 0;
	private final Label valueLabel = new Label();

	public ValueAxis(final Chart<?, ?> chart) {
		this.chart = chart;
		UI.style(this, CSS.undecorated, CSS.glass, CSS.valueAxis);

		chart.getChartContentArea().heightProperty().addListener(new ChangeListener<Number>() {
			@Override
			public void changed(final ObservableValue<? extends Number> observable, final Number oldValue, final Number newValue) {
				setHeight(newValue.doubleValue());
			}
		});
	}

	public String stringValueOf(double value) {
		String text = "";
		if (value >= 1000000000) {
			text = "B";
			value /= 1000000000;
		} else if (value >= 1000000) {
			text = "M";
			value /= 1000000;
		} else if (value >= 1000) {
			text = "K";
			value /= 1000;
		}
		return String.valueOf(Constant.numberFormat.format(value) + text);
	}

	public Chart<?, ?> getChart() {
		return chart;
	}

	public double getValueForDisplay(final double position) {
		return upperBound - (position / scale);
	}

	public double getDisplayPosition(final double value) {
		return (upperBound - value) * scale;
	}

	private void updateScale() {
		scale = getHeight() / (upperBound - lowerBound);
	}

	public void updateAxisBounds(final double upperBound, final double lowerBound) {
		this.upperBound = upperBound;
		this.lowerBound = lowerBound;
		updateScale();
		layoutAxisChildren();
	}


	protected void layoutAxisChildren() {
		final double height = getHeight();

		final int numOfTickMarks = (int) Math.floor(height / (textHeight + (padding * 2)));
		double tickRange = Math.abs(upperBound - lowerBound) / numOfTickMarks;

		for (final double tickUnitDefault : TICK_UNIT_DEFAULTS) {
			if (tickRange <= tickUnitDefault) {
				tickRange = tickUnitDefault;
				break;
			}
		}

		final double highestTickValue = Math.ceil(upperBound / tickRange) * tickRange;
		double lowestTickValue = Math.floor(lowerBound / tickRange) * tickRange;

		getChart().getGrid().clearHorizontalLines();
		getChildren().clear();

		Label label = null;
		// Line tick = null;
		if ((lowestTickValue <= highestTickValue) && (tickRange > 0)) {
			while (lowestTickValue < highestTickValue) {
				final double y = getDisplayPosition(lowestTickValue);
				if ((y >= getLayoutY()) && (y <= (getLayoutY() + getHeight()))) {
					final String text = stringValueOf(lowestTickValue);

					// tick = new Line(0, y, getChart().getChartContentArea().getWidth()-10, y);
					// getChart().getGrid().getChildren().add(tick);
					getChart().getGrid().addHorizontalLine(y);

					label = new Label(text);
					label.setMaxWidth(Double.MAX_VALUE);
					label.setMinWidth(width);
					label.setLayoutY(y - (textHeight / 2) - label.getInsets().getTop());
					label.setLayoutX(tickLength + 3);
					getChildren().add(label);
				}
				lowestTickValue += tickRange;
			}
		}
		if (!getChildren().contains(valueLabel)) {
			getChildren().add(valueLabel);
		}
	}

	public double getUpperBound() {
		return upperBound;
	}

	public double getLowerBound() {
		return lowerBound;
	}

	public boolean isZoomable() {
		return zoomable;
	}

	public void setZoomable(final boolean zoomable) {
		this.zoomable = zoomable;
	}

}
